﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IRCEngine
{
	public static class MessageConverter
	{
		const char lowLevelQuoteChar = '\x10'; // '\020'
		const char ctcpDelimChar = '\x1'; // '\001'
		const char ctcpQuoteChar = '\x5C'; // '\134'

		/// <summary>
		/// Encodes a message for client to server communication.
		/// </summary>
		/// <param name="message">The middle level message to encode.</param>
		/// <returns>The resulting low level message.</returns>
		public static string LowLevelEncode(string message)
		{
			StringBuilder builder = new StringBuilder(message.Length);
			foreach (var c in message)
			{
				switch (c)
				{
					case lowLevelQuoteChar:
						builder.Append(lowLevelQuoteChar).Append(lowLevelQuoteChar);
						break;
					case '\r':
						builder.Append(lowLevelQuoteChar).Append('r');
						break;
					case '\n':
						builder.Append(lowLevelQuoteChar).Append('n');
						break;
					case '\0':
						builder.Append(lowLevelQuoteChar).Append('0');
						break;
					default:
						builder.Append(c);
						break;
				}
			}
			return builder.ToString();
		}

		/// <summary>
		/// Decodes a message from server to client communication
		/// </summary>
		/// <param name="message">The low level message to decode.</param>
		/// <returns>The resulting middle level message.</returns>
		public static string LowLevelDecode(string message)
		{
			StringBuilder builder = new StringBuilder(message.Length);
			bool quoteChar = false;
			foreach (var c in message)
			{
				if (quoteChar)
				{
					quoteChar = false;
					switch (c)
					{
						case lowLevelQuoteChar:
							builder.Append(lowLevelQuoteChar);
							break;
						case 'r':
							builder.Append('\r');
							break;
						case 'n':
							builder.Append('\n');
							break;
						case '0':
							builder.Append('\0');
							break;
						default:
							builder.Append(c);
							break;
					}
				}
				else if (c == lowLevelQuoteChar)
				{
					quoteChar = true;
				}
				else
					builder.Append(c);
			}
			return builder.ToString();
		}

		/// <summary>
		/// Encodes a message for user to client communication.
		/// </summary>
		/// <param name="message">The user level message to encode.</param>
		/// <returns>The resulting middle level message.</returns>
		public static string CTCPEncode(string message)
		{
			StringBuilder builder = new StringBuilder(message.Length);
			foreach (var c in message)
			{
				switch (c)
				{
					case ctcpQuoteChar:
						builder.Append(ctcpQuoteChar).Append(ctcpQuoteChar);
						break;
					case ctcpDelimChar:
						builder.Append(ctcpQuoteChar).Append('a');
						break;
					default:
						builder.Append(c);
						break;
				}
			}
			return builder.ToString();
		}

		/// <summary>
		/// Decodes a message from client to user communication.
		/// </summary>
		/// <param name="message">The middle level message to decode.</param>
		/// <returns>The resulting user level message.</returns>
		public static string CTCPDecode(string message)
		{
			StringBuilder builder = new StringBuilder(message.Length);
			bool quoteChar = false;
			foreach (var c in message)
			{
				if (quoteChar)
				{
					quoteChar = false;
					switch (c)
					{
						case ctcpQuoteChar:
							builder.Append(ctcpQuoteChar);
							break;
						case 'a':
							builder.Append(ctcpDelimChar);
							break;
						default:
							builder.Append(c);
							break;
					}
				}
				else if (c == ctcpQuoteChar)
				{
					quoteChar = true;
				}
				else
					builder.Append(c);
			}
			return builder.ToString();
		}

		/// <summary>
		/// Tags a user level message as tagged data.
		/// The message is encoded before wrapping it with the delimiters.
		/// </summary>
		/// <param name="message">The user level message to encode and wrap.</param>
		/// <returns>The resulting CTCP tagged message.</returns>
		public static string CTCPTag(string message)
		{
			return ctcpDelimChar + CTCPEncode(message) + ctcpDelimChar;
		}

		/// <summary>
		/// Extracts all tagged messages from a middle level message.
		/// The returned messages are already decoded as user level messages.
		/// </summary>
		/// <param name="message">The middle level message to extract from.</param>
		/// <returns>The resulting user level tagged message.</returns>
		public static string[] ExtractCTCPMessages(string message)
		{
			List<string> ctcp = new List<string>(message.Split(ctcpDelimChar).Where((value, index) => (index % 2 == 1)).Select((value) => CTCPDecode(value)));
			return ctcp.ToArray();
		}

		/// <summary>
		/// Removes all tagged messages from a middle level message.
		/// </summary>
		/// <param name="message">The middle level message to remove in.</param>
		/// <returns>The resulting tagged data free middle level message.</returns>
		public static string RemoveCTCPMessages(string message)
		{
			List<string> normal = new List<string>(message.Split(ctcpDelimChar).Where((value, index) => (index % 2 == 0)));
			return string.Join("", normal);
		}

		private static System.Text.RegularExpressions.Regex formatCharMatch = new System.Text.RegularExpressions.Regex("\x2|\x9|\xf|\x16|\x1f|\x3(?:(?:0?\\d|1[0-5])?(?:,(?:0?\\d|1[0-5]))?)", System.Text.RegularExpressions.RegexOptions.Compiled | System.Text.RegularExpressions.RegexOptions.ExplicitCapture);

		/// <summary>
		/// Clears the input message from any IRC format character.
		/// </summary>
		/// <param name="message">The message to clean.</param>
		/// <returns>The cleaned message.</returns>
		public static string RemoveFormatChars(string message)
		{
			return formatCharMatch.Replace(message, string.Empty);
		}
	}
}
